﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "LevelUtils.h"
#include "NewLevel.h"
#include "LevelNameManager.h"
#include "GbmpBuiltInCategoryUniIdentitiesNew.h"
#include "StoreyLevelComparer.h"

//平台头文件
#include "IDocument.h"
#include "IGenericElement.h"
#include "IElementBasicInformation.h"
#include "GbmpDocUtils.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;
using namespace std;

std::wstring gcmp::LevelUtils::GetLastLevelPrefix(const IDocument *pDocument, const ElementId &buildingId)
{
    const LevelNameManager *pLevelNameMgr = LevelNameManager::Get(pDocument);
    DBG_WARN_AND_RETURN_UNLESS(pLevelNameMgr, L"", L"标高命名管理为空，应该在文档初始化时就创建好了",L"GDMPLab",L"2024-03-30");
    return pLevelNameMgr->GetPrefix(buildingId);
}

std::wstring gcmp::LevelUtils::GetLastLevelSuffix(const IDocument *pDocument, const ElementId &buildingId)
{
    const LevelNameManager *pLevelNameMgr = LevelNameManager::Get(pDocument);
    DBG_WARN_AND_RETURN_UNLESS(pLevelNameMgr, L"", L"标高命名管理为空，应该在文档初始化时就创建好了",L"GDMPLab",L"2024-03-30");
    return pLevelNameMgr->GetSuffix(buildingId);
}

int gcmp::LevelUtils::GetLastLevelNumber(const IDocument *pDocument, const ElementId &buildingId)
{
    const LevelNameManager *pLevelNameMgr = LevelNameManager::Get(pDocument);
    DBG_WARN_AND_RETURN_UNLESS(pLevelNameMgr, 0, L"标高命名管理为空，应该在文档初始化时就创建好了",L"GDMPLab",L"2024-03-30");
    return pLevelNameMgr->GetNumber(buildingId);
}

int gcmp::LevelUtils::GetNextLevelNumber(const IDocument *pDocument, const ElementId &buildingId)
{
    const LevelNameManager *pLevelNameMgr = LevelNameManager::Get(pDocument);
    DBG_WARN_AND_RETURN_UNLESS(pLevelNameMgr, 0, L"标高命名管理为空，应该在文档初始化时就创建好了",L"GDMPLab",L"2024-03-30");
    return pLevelNameMgr->GetNextNumber(buildingId);
}

LevelName gcmp::LevelUtils::GetNextLevelName(IDocument *pDocument, const ElementId &buildingId)
{
    std::wstring prefix = GetLastLevelPrefix(pDocument, buildingId);
    int number = GetNextLevelNumber(pDocument, buildingId);
    std::wstring suffix = GetLastLevelSuffix(pDocument, buildingId);
    while (GetLevelIdByName(pDocument, prefix + StringUtil::ToWString(number) + suffix).IsValid())
    {
        ++number;
    }
    return LevelName(prefix, number, suffix);
}

ElementId gcmp::LevelUtils::GetLevelIdByName(IDocument *pDocument, const std::wstring &levelName)
{
    std::vector<IElement*> allLevels = gcmp::GbmpDocUtils::GetElementsByCategory(pDocument, BuiltInCategoryUniIdentities::BICU_NEW_ELEVATION);
    FOR_EACH(pElement, allLevels)
    {
        if (pElement && pElement->GetBasicInformation())
        {
            if (!pElement->GetBasicInformation()->GetIsTransient() && pElement->GetBasicInformation()->GetName() == levelName)
            {
                return pElement->GetElementId();
            }
        }
    }

    return ElementId::InvalidID;
}

//////////////////////////////////////////////////////////////////////////
ILevel* LevelUtils::GetLevelById(const IDocument *pDocument, const ElementId &levelId)
{
    IElement *pElement = pDocument->GetElement(levelId);
    if (!pElement)
    {
        return nullptr;
    }
    IGenericElement *pGenericElement = quick_cast<IGenericElement>(pElement);
    if (!pGenericElement)
    {
        return nullptr;
    }
    ILevel *pILevel = quick_cast<ILevel>(pGenericElement->GetExternalObject());
    if (!pILevel)
    {
        return nullptr;
    }
    return pILevel;
}

std::vector<const ILevel*> LevelUtils::GetLevels(const IDocument *pDocument)
{
    std::vector<const ILevel*> levels;
    std::vector<IElement*> levelElements = gcmp::GbmpDocUtils::GetElementsByCategory(pDocument, BuiltInCategoryUniIdentities::BICU_NEW_ELEVATION);
    FOR_EACH (pLevelElement, levelElements)
    {
        const IGenericElement *pGenericElement = quick_cast<const IGenericElement>(pLevelElement);
        if (!pGenericElement)
        {
            continue;
        }
        const ILevel *pILevel = quick_cast<const ILevel>(pGenericElement->GetExternalObject());
        if (!pILevel)
        {
            continue;
        }

        levels.push_back(pILevel);
    }
    return levels;
}

std::vector<const ILevel*> LevelUtils::GetLevelsFromHighToLow(const IDocument *pDocument)
{
    std::vector<const ILevel*> levels = GetLevels(pDocument);
    std::sort(levels.begin(), levels.end(), HigherLevelComparer());

    return levels;
}

std::vector<const ILevel*> LevelUtils::GetLevelsFromLowToHigh(const IDocument *pDocument)
{
    std::vector<const ILevel*> levels = GetLevels(pDocument);
    std::sort(levels.begin(), levels.end(), LowerLevelComparer());

    return levels;
}

std::vector<const ILevel*> LevelUtils::GetStoreyLevels(const IDocument *pDocument)
{
    std::vector<const ILevel*> storeyLevels;
    std::vector<const ILevel*> allILevels = GetLevels(pDocument);
    FOR_EACH (pILevel, allILevels)
    {
        if (!pILevel->GetBuildingId().IsValid())
        {
            continue;
        }
        storeyLevels.push_back(pILevel);
    }
    return storeyLevels;
}

std::vector<const ILevel*> LevelUtils::GetStoreyLevelsInBuilding(const IDocument *pDocument, ElementId buildingId)
{
    std::vector<const ILevel*> storeyLevels;
    std::vector<const ILevel*> allILevels = GetLevels(pDocument);
    FOR_EACH(pILevel, allILevels)
    {
        if (pILevel->GetBuildingId() == buildingId)
        {
            storeyLevels.push_back(pILevel);
        }
    }
    return storeyLevels;
}

std::vector<const ILevel*> LevelUtils::GetStoreyLevelsFromHighToLow(const IDocument *pDocument)
{
    std::vector<const ILevel*> levels = GetStoreyLevels(pDocument);
    std::sort(levels.begin(), levels.end(), HigherStoreyComparer());
    return levels;
}

std::vector<const ILevel*> gcmp::LevelUtils::GetStoreyLevelsFromLowToHigh(const IDocument *pDocument)
{
    std::vector<const ILevel*> levels = GetStoreyLevels(pDocument);
    std::sort(levels.begin(), levels.end(), LowerStoreyComparer());
    return levels;
}


std::vector<const ILevel*> gcmp::LevelUtils::GetReferenceLevels(const IDocument *pDocument)
{
    std::vector<const ILevel*> referenceILevels;
    std::vector<const ILevel*> allILevels = GetLevels(pDocument);
    FOR_EACH(pILevel, allILevels)
    {
        if (pILevel->GetBuildingId().IsValid())
        {
            continue;
        }
        referenceILevels.push_back(pILevel);
    }
    return referenceILevels;
}

std::vector<const ILevel*> gcmp::LevelUtils::GetReferenceLevelsFromHighToLow(const IDocument *pDocument)
{
    std::vector<const ILevel*> referenceILevels = GetReferenceLevels(pDocument);
    std::sort(referenceILevels.begin(), referenceILevels.end(), HigherLevelComparer());
    return referenceILevels;
}

std::vector<const ILevel*> gcmp::LevelUtils::GetReferenceLevelsFromLowToHigh(const IDocument *pDocument)
{
    std::vector<const ILevel*> referenceILevels = GetReferenceLevels(pDocument);
    std::sort(referenceILevels.begin(), referenceILevels.end(), LowerLevelComparer());
    return referenceILevels;
}

const ILevel* LevelUtils::GetLowerStoreyLevel(const IDocument *pDocument, double elevation)
{
    const ILevel* pLowerLevel = nullptr;

    std::vector<const ILevel*> allLevels = GetLevels(pDocument);
    std::sort(allLevels.begin(), allLevels.end(), HigherLevelComparer());
    FOR_EACH (pLevel, allLevels)
    {
        DBG_WARN_AND_CONTINUE_UNLESS(pLevel, L"pLevel为空",L"GDMPLab",L"2024-03-30");
        if (!pLevel->GetBuildingId().IsValid())
            continue;
        if (MathUtils::IsGreaterThan(pLevel->GetElevation(), elevation, Constants::LENGTH_EPS) ||
            MathUtils::IsEqual(pLevel->GetElevation(), elevation, Constants::LENGTH_EPS))
            continue;

        pLowerLevel = pLevel;
        break;
    }

    return pLowerLevel;
}

const ILevel* LevelUtils::GetHigherStoreyLevel(const IDocument *pDocument, double elevation)
{
    const ILevel* pHigherLevel = nullptr;

    std::vector<const ILevel*> allLevels = GetLevels(pDocument);
    std::sort(allLevels.begin(), allLevels.end(), LowerLevelComparer());
    FOR_EACH(pLevel, allLevels)
    {
        DBG_WARN_AND_CONTINUE_UNLESS(pLevel, L"pLevel为空",L"GDMPLab",L"2024-03-30");
        if (!pLevel->GetBuildingId().IsValid())
            continue;
        if (MathUtils::IsLessThan(pLevel->GetElevation(), elevation, Constants::LENGTH_EPS) ||
            MathUtils::IsEqual(pLevel->GetElevation(), elevation, Constants::LENGTH_EPS))
            continue;

        pHigherLevel = pLevel;
        break;
    }

    return pHigherLevel;
}

const ILevel* LevelUtils::GetLowerStoreyLevel(const IDocument *pDocument, double elevation, ElementId buildingId)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDocument, L"pDocument为空",L"GDMPLab",L"2024-03-30");
    std::vector<const ILevel*> buildingLevels;
    std::vector<const ILevel*> allLevels = GetLevels(pDocument);
    FOR_EACH(pLevel, allLevels)
    {
        DBG_WARN_AND_CONTINUE_UNLESS(pLevel, L"pLevel为空",L"GDMPLab",L"2024-03-30");
        if (!pLevel->GetBuildingId().IsValid())
            continue;
        if (pLevel->GetBuildingId() != buildingId)
            continue;

        buildingLevels.push_back(pLevel);
    }

    const ILevel* pLowerBuildingLevel = nullptr;
    std::sort(buildingLevels.begin(), buildingLevels.end(), HigherLevelComparer());
    FOR_EACH(pBuildingLevel, buildingLevels)
    {
        DBG_WARN_AND_CONTINUE_UNLESS(pBuildingLevel, L"pBuildingLevel为空",L"GDMPLab",L"2024-03-30");
        if (MathUtils::IsGreaterThan(pBuildingLevel->GetElevation(), elevation, Constants::LENGTH_EPS) ||
            MathUtils::IsEqual(pBuildingLevel->GetElevation(), elevation, Constants::LENGTH_EPS))
            continue;

        pLowerBuildingLevel = pBuildingLevel;
        break;
    }
    return pLowerBuildingLevel;
}

const ILevel* LevelUtils::GetHigherStoreyLevel(const IDocument *pDocument, double elevation, ElementId buildingId)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDocument, L"pDocument为空",L"GDMPLab",L"2024-03-30");
    std::vector<const ILevel*> buildingLevels;
    std::vector<const ILevel*> allLevels = GetLevels(pDocument);
    FOR_EACH(pLevel, allLevels)
    {
        DBG_WARN_AND_CONTINUE_UNLESS(pLevel, L"pLevel为空",L"GDMPLab",L"2024-03-30");
        if (!pLevel->GetBuildingId().IsValid())
            continue;
        if (pLevel->GetBuildingId() != buildingId)
            continue;

        buildingLevels.push_back(pLevel);
    }

    const ILevel* pHigherBuildingLevel = nullptr;
    std::sort(buildingLevels.begin(), buildingLevels.end(), LowerLevelComparer());
    FOR_EACH(pBuildingLevel, buildingLevels)
    {
        DBG_WARN_AND_CONTINUE_UNLESS(pBuildingLevel, L"pBuildingLevel为空",L"GDMPLab",L"2024-03-30");
        if (MathUtils::IsLessThan(pBuildingLevel->GetElevation(), elevation, Constants::LENGTH_EPS) ||
            MathUtils::IsEqual(pBuildingLevel->GetElevation(), elevation, Constants::LENGTH_EPS))
            continue;

        pHigherBuildingLevel = pBuildingLevel;
        break;
    }
    return pHigherBuildingLevel;
}

bool LevelUtils::GetLevelElevation(const IDocument* pDoc, ElementId levelId, double& elevation)
{
    const ILevel* pLevel = GetLevelById(pDoc, levelId);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pLevel, L"pLevel为空",L"GDMPLab",L"2024-03-30");

    elevation = pLevel->GetElevation();
    return true;
}

bool LevelUtils::DeleteLevelForcely(IDocument *pDocument, const ElementId& levelId, std::wstring* cannotDeleteExplanation)
{
    bool isSuccess = false;
    if (const IElement* pElement = pDocument->GetElement(levelId))
    {
        const IElementDeletionComponent* opDeletionComponent = pElement->GetElementDeletionComponent();
        if (opDeletionComponent)
        {
            if (opDeletionComponent->CanBeDeleted(nullptr,cannotDeleteExplanation))
            {
                isSuccess = pDocument->DeleteElement(pElement->GetElementId(), cannotDeleteExplanation);
            }
        }
        else
        {
            isSuccess = pDocument->DeleteElement(pElement->GetElementId(), cannotDeleteExplanation);
        }
    }

    return isSuccess;
}
